package com.leyou.order.service;

import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.threadlocals.UserHolder;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.order.vo.OrderDetailVO;
import com.leyou.order.vo.OrderLogisticsVO;
import com.leyou.order.vo.OrderVO;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
//import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单Service
 */
@Slf4j
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private PayHelper payHelper;

    /**
     * 创建订单
     * @param orderDTO  接收参数: 支付类型、购物车数据、地址数据
     * @return  订单编号
     */
//    @GlobalTransactional    //seata的分布式事务注解
    @Transactional
    public Long createOrder(OrderDTO orderDTO) {
        //订单ID
        long orderId = idWorker.nextId();
        //当前用户
        UserInfo user = UserHolder.getUser();
        //实例化订单对象
        Order order = new Order();
        order.setOrderId(orderId);                      //订单ID
        order.setPostFee(0L);                           //邮费
        order.setPaymentType(1);                        //付款方式(1.在线支付,2.货到付款)
        order.setUserId(user.getId());                  //当前用户id
        order.setStatus(OrderStatusEnum.INIT.value());  //订单状态
        order.setInvoiceType(0);                        //发票类型: 0无发票,1普通发票,2电子发票
        order.setSourceType(2);                         //订单来源: 1.app端,2pc端,3微信端

        order.setActualFee(1L);                         //实付金额: 后续微信支付(总金额 - 活动金额)

        //获取orderDTO中的购物车信息,取出skuids集合
        List<CartDTO> carts = orderDTO.getCarts();
        //批量查询sku,算出金额
        List<Long> skuIds = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
        //批量查询skuDTO集合
        List<SkuDTO> skuDTOList = itemClient.querySkuByIds(skuIds);
        //计算所有sku的价格
        Map<Long, Integer> cartMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
        long total = skuDTOList.stream().mapToLong(skuDTO -> skuDTO.getPrice() * cartMap.get(skuDTO.getId())).sum();

        order.setTotalFee(total);                        //商品金额

        //保存订单表
        int count = orderMapper.insertSelective(order);
        if (count != 1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //========================================================================

        //保存订单详情
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (SkuDTO skuDTO : skuDTOList) {
            OrderDetail od = new OrderDetail();
            od.setId(idWorker.nextId());
            od.setOrderId(orderId);
            od.setSkuId(skuDTO.getId());
            od.setNum(cartMap.get(skuDTO.getId()));
            od.setTitle(skuDTO.getTitle());
            od.setPrice(skuDTO.getPrice());
            od.setOwnSpec(skuDTO.getOwnSpec());
            od.setImage(StringUtils.substringBefore(skuDTO.getImages(), ","));
            orderDetailList.add(od);
        }
        int orderDetailCount = orderDetailMapper.insertList(orderDetailList);
        if (orderDetailCount != orderDetailList.size()){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //========================================================================

        //保存物流表信息
        //查询地址信息
        AddressDTO addressDTO = userClient.queryAddressById(user.getId(), orderDTO.getAddressId());
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
        orderLogistics.setOrderId(orderId); //设置订单编号
        int orderLogisticsCount = orderLogisticsMapper.insertSelective(orderLogistics);
        if (orderLogisticsCount != 1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //减库存
        itemClient.minusStock(cartMap);
//        int i = 1/0;      模拟分布式异常

        //返回订单ID
        return orderId;
    }


    /**
     * 根据id查询订单
     * @param id    订单id
     * @return      订单VO对象
     */
    public OrderVO queryOrderById(Long id) {
        //1.查询订单
        Order order = orderMapper.selectByPrimaryKey(id);
        if (order == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //2.判断订单归属
        UserInfo user = UserHolder.getUser();
        if (!order.getUserId().equals(user.getId())){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //3.查询订单条目
        OrderDetail od = new OrderDetail();
        od.setOrderId(id);
        List<OrderDetail> orderDetailList = orderDetailMapper.select(od);
        if (CollectionUtils.isEmpty(orderDetailList)){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //4.查询物流信息
        OrderLogistics orderLogistics = orderLogisticsMapper.selectByPrimaryKey(id);
        if (orderLogistics == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //5.把数据封装到VO中
        OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
        orderVO.setDetailList(BeanHelper.copyWithCollection(orderDetailList, OrderDetailVO.class));
        orderVO.setLogistics(BeanHelper.copyProperties(orderLogistics, OrderLogisticsVO.class));
        //6.返回OrderVO对象
        return orderVO;
    }


    private static final String KEY_PREFIX = "ly:order:url";

    /**
     * 获取支付链接
     * @param orderId   订单id
     * @return  支付链接
     */
    public String getPayUrl(Long orderId) {
        //根据id查询订单: 先去redis中查找
        String key = KEY_PREFIX  + orderId;

        try {
            String cacheUrl = stringRedisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(cacheUrl)){
                return cacheUrl;
            }
        } catch (Exception e){
            e.printStackTrace();
            log.error("【订单微服务】生成二维码链接, redis挂壁");
        }

        //判断订单状态
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (!OrderStatusEnum.INIT.value().equals(order.getStatus())){
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }

        //调用微信支付接口生成支付链接
        String payUrl = payHelper.getPayUrl(orderId, order.getActualFee(), "【乐优商城】商品购买信息");

        try {
            stringRedisTemplate.opsForValue().set(key,payUrl,2, TimeUnit.HOURS);
        } catch (Exception e){
            e.printStackTrace();
            log.error("【订单微服务】生成二维码链接, redis挂壁");
        }

        //返回支付链接

        return payUrl;
    }

    /**
     * 接收微信异步通知结果
     * @param data
     */
    @Transactional
    public Map<String,String> handlerNotify(Map<String, String> data) {
        System.out.println("来了没有----->data = " + data);
        //返回收到通知信息给微信
        Map<String,String> msg = new HashMap<>();
        msg.put("return_code","SUCCESS");
        msg.put("return_msg","OK");
        try {
            //校验业务标识
            payHelper.checkResultCode(data);
            //校验签名
            payHelper.checkSignature(data);

            //获取支付信息 (订单id 和 支付金额)
            String out_trade_no = data.get("out_trade_no");
            String total_fee = data.get("total_fee");
            if (StringUtils.isBlank(out_trade_no) || StringUtils.isBlank(total_fee)){
                throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
            }
            Long orderId = Long.valueOf(out_trade_no);  //订单id
            Long totalFee = Long.valueOf(total_fee);    //支付金额
            //查询订单
            Order order = orderMapper.selectByPrimaryKey(orderId);
            //校验金额
            if (!totalFee.equals(order.getActualFee())){
                throw new LyException(ExceptionEnum.INVALID_PARAM_ERROR);
            }
            //幂等校验(如果订单已经是已支付状态直接返回成功)
            if (!OrderStatusEnum.INIT.value().equals(order.getStatus())){
                return msg;
            }
            //支付成功, 修改订单状态
            Order record = new Order();
            record.setOrderId(orderId);
            record.setStatus(OrderStatusEnum.PAY_UP.value());
            record.setPayTime(new Date());
            int count = orderMapper.updateByPrimaryKeySelective(record);
            if (count != 1){
                throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
            }
            log.info("【乐优支付】处理微信支付结果成功");
        } catch (Exception e) {
            log.error("【乐优支付】处理微信支付结果出异常: {}",e.getMessage());
        }
        return msg;
    }

    /**
     * 查询支付状态接口
     * @return
     */
    public Integer queryPayState(Long orderId) {
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }
}